﻿#pragma once

#include <functional>
#include <stack>
#include <string>
#include <vector>

#include "kernel/kernel.h"

/**
 * 前向声明，预定义一个用于处理渲染的类名（根据是否是 Qt 渲染进行区分）.
 */
class MainOpenGLWidget;

namespace CAGD
{

/**
 * 前向声明.
 */
class AIS_Base;
class Command;

/**
 * @brief 屏幕点类型
 *
 */
struct Point
{
    int x, y;
};

struct Size
{
    unsigned int x, y;
};

/**
 * 坐标轴类型.
 */
struct Axis
{
    BVec x, y, z;
    BPnt origin;
};

/**
 * 按键类型.
 */
enum class Button
{
    LEFT,
    RIGHT,
    MIDDLE
};

/**
 * 键盘类型.
 */
enum class Key
{
    NONE,
    SPACE,
    ESC,
    SHIFT,
    CTRL,
    ALT,
    A,
    B,
    C,
    D,
    E,
    F,
    S,
    V,
    X,
    Y,
    Z,
    K0,
};

/**
 * @brief 鼠标状态类
 *
 */
enum class MouseStatus
{
    NONE,
    MOVE,
    HANG,
    TOUCH
};

/**
 * @brief 视图状态类
 *
 */
enum class ViewMode
{
    OBSERVE,
    CONTROL,
    INFO
};

/**
 * @brief 渲染模式类
 *
 */
enum class ShadeMode
{
    FILL,
    LINE
};

/**
 * @brief 相机模式类
 *
 */
enum class CameraMode
{
    ROTATE,
    TRANSLATE
};

/**
 * 信息类型.
 */
struct ContextInfo
{
    MouseStatus mouseStatus; // 鼠标状态
    ViewMode viewMode;       // 观察模式
    ShadeMode shadeMode;     // 渲染模式
    CameraMode cameraMode;   // 相机模式
    unsigned int linewidth;  // 线宽
    unsigned int zebra;      // 斑马纹细度
    unsigned int skyboxTex;  // 天空盒纹理
    float depth[2];          // 记录深度
    Size size;               // 窗口尺寸
    Point points[3];         // 左中右键位置
    Key key;                 // 当前按键
    BPntf originPos;         // 原点位置
    BPntf viewPos;           // 相机位置
    BMat4x4 viewMat;         // 视图矩阵
    BMat4x4 projectionMat;   // 投影矩阵
};

//////////////////////////////////////////////////////////////////////////////////////////
///
///

/**
 * @brief 交互类型
 *
 */
class AIS_Context
{
  protected:
    /**
     * @brief 标准交互容器.
     * @param parent
     * @see
     */
    AIS_Context(MainOpenGLWidget *parent);

    /**
     * @brief 移除拷贝构造.
     * @param context
     * @see
     */
    AIS_Context(const AIS_Context &context) = delete;

  public:
    /**
     * @brief 创建并分享 Context.
     * @return
     * @see
     */
    static AIS_Context &ShareContext(MainOpenGLWidget *parent = nullptr);

    /**
     * @brief 初始化 Context.
     * @see
     */
    void InitContext();

    /**
     * @brief 删除 Context.
     * @see
     */
    void DeleteContext();

    /**
     * @brief 鼠标按下操作
     *
     */
    void MousePressEvent(Point pos, Button button);

    /**
     * @brief 鼠标释放操作.
     * @param pos
     * @see
     */
    void MouseReleaseEvent(Point pos);

    /**
     * @brief 鼠标移动操作.
     * @param pos
     * @see
     */
    void MouseMoveEvent(Point pos);

    /**
     * @brief 鼠标滚轮操作.
     * @param angleDelta
     * @see
     */
    void MouseWheelEvent(Point angleDelta);

    /**
     * @brief 按键操作.
     * @param key
     * @see
     */
    void KeyPressEvent(Key key);

    /**
     * @brief 按键操作.
     * @param key
     * @see
     */
    void KeyReleaseEvent(Key key);

    /**
     * @brief 绘制操作.
     * @see
     */
    void PaintEvent();

    /**
     * @brief 窗口形变操作.
     * @param width
     * @param height
     * @see
     */
    void ResizeEvent(int width, int height);

    /**
     * @brief 创建命令.
     * @param cmd
     * @see
     */
    void CreateCommand(std::string cmd);

    /**
     * @brief 创建物体.
     * @param object
     * @see
     */
    void CreateObject(Handle(AIS_Base) object);

    /**
     * @brief 将屏幕点转换为空间点.
     * @return
     * @param pos
     * @see
     */
    BPntf GetSpacePoint(Point pos, float z = 2.0) const;

    /**
     * @brief 将空间点转换为屏幕点.
     * @return
     * @param pos
     * @param depth
     * @see
     */
    BPnt2df GetScreenPoint(BPntf pos, float &depth) const;

    /**
     * @brief 获得深度信息
     *
     * @return double
     */
    float GetDepth() const;

    /**
     * @brief 设置深度.
     * @param depth
     * @see
     */
    void SetDepth(float depth);

    /**
     * @brief 获得选择的物体.
     * @return
     * @see
     */
    Handle(AIS_Base) GetSelected() const;

    /**
     * @brief 获得所有物体.
     * @return
     * @see
     */
    std::vector<Handle(AIS_Base)> &GetObjects();

    /**
     * @brief 获得交互信息.
     * @return
     * @see
     */
    ContextInfo &GetInfo();

  protected:
    /**
     * @brief 调整相机.
     * @see
     */
    void UpdateCamera();

    /**
     * @brief 调整相机.
     * @param dxy
     * @param delta
     * @see
     */
    void UpdateCamera(Point dxy, Point delta);

    /**
     * @brief 清除深度.
     * @see
     */
    void ClearDepth();

    /**
     * @brief 清空消亡物体
     *
     */
    void ClearDeadObjects();

    /**
     * @brief 清空所有物体.
     * @see
     */
    void ClearAllObjects();

    /**
     * @brief 点击检测
     *
     * @param pos
     */
    void ClickTest(Point pos);

    /**
     * @brief 移动检测.
     * @param pos
     * @see
     */
    void MoveTest(Point pos);

  protected:
    ContextInfo m_info;
    MainOpenGLWidget *m_parent;

    Handle(AIS_Base) m_selectObj;                 // 正在选中的物体
    std::vector<Handle(AIS_Base)> m_selectedObjs; // 已经选中的所有物体
    std::stack<Handle(Command)> m_cmds;           // 栈中的命令
    std::vector<Handle(AIS_Base)> m_objects;      // 所有物体
};

} // namespace CAGD
